在 Arch Linux (Manjaro) 中使用 KVM 虚拟机

介绍

QEMU 是 Quick Emulator 的缩写。从名称可以看出,它是一个模拟器。从功能上看,它可以在一种架构的硬件上模拟其他架构的硬件,比如 Android 开发时所用的官方模拟器底层即是基于 QEMU 的,它在 x86 平台上模拟 ARM。

为实现模拟不同架构硬件的功能,QEMU 会对 CPU 指令进行翻译,涉及到内核与用户代码的转换,性能较低。但另一个优势由于 QEMU 不涉及到宿主机的内核,可以完全在用户态运行,这样可以让它在一些受限场景下使用 (比如无法在宿主机安装驱动的环境)。

在宿主机与虚拟机是相同架构时,它们的 CPU 指令几乎完全相同,完全不需要指令翻译,可以完全透传给宿主机硬件。省去指令翻译与内核与用户代码的转换后可以达到接近宿主机性能。

所以在 Android 开发时,有时会使用 genymotion 之类的模拟器,这些模拟器是基于 Android x86 平台的,不需要跨架构的指令翻译。

macOS 上的 iOS 模拟器不是模拟了 ARM 平台,在模拟器中跑的是 x86 平台,所以不能安装网络上下载的 ipa 格式的 iOS 软件包。

QEMU 可以使用其他虚拟机管理程序,比如 Xen 或 KVM 来使用虚拟化技术,达到接近宿主机的性能。

KVM 是 Kernel-based Virtual Machine 的缩写,即内核级虚拟机。

环境准备

如标题所述,这里使用 Arch Linux (Manjaro 是一样的) 作为宿主机操作系统。

环境检查

使用以下命令检查是否支持硬件虚拟化技术:

1
2
3
4
5
# 这里输出几行内容表示支持硬件虚拟化技术
grep -E "(vmx|svm)" --color=always /proc/cpuinfo
# 或者使用,输出 Virtualization 技术的名称
LC_ALL=C lscpu | grep Virtualization

KVM 虚拟化依赖硬件虚拟化技术,需要使用支持虚拟化技术的 CPU 并在 BIOS 中开启虚拟化功能。

虚拟化技术在各个厂商这里名称不同,Intel 这里称为 Intel VT,AMD 称之为 SVM。

我的 CPU 为 Intel J1900@2.0GHz 4 Core。
查询 Intel Product Specifications 可以看到这块 CPU 支持 Intel® Virtualization Technology (VT-x) 技术,不支持 Intel® Virtualization Technology for Directed I/O (VT-d) 。

VT-x 也就是我们常说的硬件虚拟化,VT-d 用于试宿主机硬件直通虚拟机。

这里只需要硬件虚拟化技术,所以是满足 KVM 的使用要求的。

检查内核

1
2
3
4
5
6
7
8
# 这里输出中需要包含 kvm kvm_intel
lsmod | grep kvm
# 这里输出中需要包含 virtio
lsmod | grep virtio
# 没有 virtio 时执行
sudo modprobe virtio

安装所需的软件包

执行下面的指令来安装所需的软件包:

1
2
3
4
5
#
yay -Sy qemu libvirt ebtables dnsmasq bridge-utils openbsd-netcat
# 图形化的管理界面,如果使用带桌面环境的操作系统可选安装。我这里不安装
# yay -Sy virt-manager

  • qemu QEMU 软件,提供 qemu-img 等命令
  • libvirt 提供管理虚拟机、存储、网络的功能
  • ebtables 桥接网络管理,用于 default NAT 网络
  • dnsmasq DHCP DNS 服务,用于 default NAT 网络
  • bridge-utils 桥接网络管理,用于桥接网络
  • openbsd-netcat 用于通过 SSH 管理

启动服务

执行以下命令启动所需服务:

1
2
3
4
# 启动虚拟机管理后台服务
sudo systemctl start libvirtd
# 启动 default NAT 网络
sudo virsh net-start default

按照以上方式安装好软件包之后,应当已经存在 default network,若启动上述服务时出现错误可使用以下命令检查:

1
2
3
4
5
# 列出网络
sudo virsh net-list --all
# 若没有任何网络,可手动定义网络
sudo virsh net-define /etc/libvirt/qemu/networks/default.xml

下载 virtio 驱动

在 KVM 中使用 virtio 设备将 io 设备 (例如:磁盘、网卡等) 半虚拟化可以提高虚拟机的 io 性能,但需要在虚拟机中安装 virtio 设备驱动。

https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.164-2/ 下载:

1
2
3
virtio-win-0.1.164.iso
virtio-win-0.1.164_amd64.vfd
virtio-win-0.1.164_x86.vfd

两个 vfd 分别 Windows 为 32 位系统与 64 位系统的 KVM 使用的软盘驱动,iso 为驱动光盘。

安装虚拟机

通过以下命令安装虚拟机,这里安装 Windows 7 x86 作为示例:

1
2
3
4
# 创建磁盘,这里创建一个 40G 的 qcow2 磁盘 (Copy on Write)
qemu-img create -f qcow2 win7.qcow2 40G
# 安装虚拟机
sudo virt-install --name win7 --memory 1024 --vcpus 2 --cpu host --cdrom GRMCENVOL_ZH-CN_PIP.iso --os-variant win7 --disk win7.qcow2,bus=virtio --disk virtio-win-0.1.164_x86.vfd,device=floppy --network network=default,model=virtio --graphics vnc,listen=0.0.0.0,port=5920

以上命令通过参数指定了:

  • 虚拟机名称为 win7
  • 内存 1024M,即 1G
  • 虚拟机 CPU 2核心
  • 虚拟机 CPU 指定为透传宿主机
  • 安装光盘镜像
  • 操作系统类型指定为 Windows 7
  • 磁盘路径与总线类型为 virtio
  • virtio x86 驱动软盘
  • 网络为 default NAT,总线类型为 virtio
  • VNC 远程访问监听地址与端口

执行命令后,可使用 vnc 客户端连接宿主机的 5920 端口看到系统已经启动,按照步骤安装即可。

需要注意的是,由于我们使用了 virtio 磁盘,在选择安装位置时是找不到磁盘的。需要点击加载驱动程序,然后扫描后会列出软盘中的驱动程序。此时选择 VirtIO SCSI controller Win7 对应的驱动,点击下一步 (此时也可一并选择显卡与网卡对应的驱动进行安装,一并安装),就可以看到磁盘了。新建分区、安装即可。

安装驱动

安装完成后进入系统,先关机,然后执行:

1
sudo virsh edit win7

此时进入 vi 编辑 win7 虚拟机的配置 xml。找到 cdrom 章节,将系统镜像路径改为 virtio 驱动光盘 iso 镜像的路径,保存退出。

执行以下命令开机:

1
sudo virsh start win7

进入到 win7 的设备管理器,安装其他设备的驱动 (在光盘中搜索即可)。

异常处理

虚拟机时钟问题

如果发现 win7 的运行速度飞快,动画加速,时钟比真实时间跑的快,时钟异常。好像安装了加速齿轮一样。可进行以下操作:

  1. 在客户机 cmd 中以管理员权限执行:

    1
    2
    # 返回 操作成功完成。
    bcdedit /set {default} useplatformclock yes
  2. 关闭虚拟机

  3. 编辑虚拟机配置
    1
    sudo virsh edit win7

找到 <clock 节,当前应该是这样的:

1
2
3
4
5
6
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
<timer name='hypervclock' present='yes'/>
</clock>

将后面的 3 个 timer 删除,也就是改为:

1
2
3
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
</clock>

保存退出,然后启动虚拟机,可以看到虚拟机时钟已经恢复了正常。

其他安装注意事项

安装 Windows 8 以上系统时,在安装好之后应当关闭 快速启动,在 控制面板 -> 电源选项 中。

其他说明

气球设备

在安装驱动时,有一个 balloon 设备,也就是气球设备,气球设备允许在客户机运行时修改客户机的内存大小:

1
2
# 将内存限制为 512M
virsh qemu-monitor-command --domain win7 --hmp 'balloon 512'

嵌套虚拟化

启用嵌套虚拟化功能:

1
2
sudo modprobe -r kvm_intel
sudo modprobe kvm_intel nested=1

永久生效:

1
2
3
sudo vim /etc/modprobe.d/modprobe.conf
# 修改或添加以下内容
options kvm_intel nested=1

检查嵌套虚拟化是否激活:

1
2
# 输出 nested = "Y"
systool -m kvm_intel -v | grep nested

磁盘缓存

推荐使用 none;提高客户机性能可选 unsafe;提高安全性可选 writeback。

可参考 虚拟化调试和优化指南 6.3. 缓存

后期转换磁盘总线

如果安装时选择了 SCSI 磁盘总线,安装后改为 virtio 时。由于没有对应的驱动,系统将启动不了。可按照以下方法安装 virtio 磁盘驱动:

  1. 若已修改了磁盘总线为 virtio,需要将原系统磁盘改回 SCSI;
  2. 为客户机创建一个新磁盘;
  3. 通过 virt-manager 或编辑 xml 添加新磁盘到客户机,使用 virtio 总线;
  4. 启动客户机,在 设备管理器 通过光盘安装新磁盘的 virtio 驱动;
  5. 关机,移除并删除新磁盘;
  6. 启动客户机,此时由于系统已有 virtio 驱动,可以正常启动。

完全使用命令安装 Linux 系统

这里以 CentOS 7 为例:

1
sudo virt-install --name centos --memory 1024 --vcpus 2 --cpu host --location CentOS-7-x86_64-Minimal-1810.iso --os-variant 'centos7.0' --disk centos.qcow2,bus=virtio --network network=default,model=virtio --graphics none --extra-args console=ttyS0
  • 这里使用 --location 而不是 --cdrom 为了支持 --extra-args 内核扩展参数;
  • --location 配合 iso 使用时 virt-install 会使用 isoinfo 命令来从 iso 获取信息,所以必须安装 yay -Sy cdrtools

其他命令

一些有用的其他命令:

1
2
3
4
5
6
7
8
9
10
11
12
# 列出虚拟机
virsh list --all
# 导出虚拟机定义,可将磁盘文件与导出的定义复制到其他机器使用
virsh dumpxml win7 > win7.xml
# 导入虚拟机
virsh define --file=win7.xml
# 给虚拟机发送关机指令
virsh shutdown win7
# 强制关闭虚拟机 (直接断电)
virsh destory win7
# 删除虚拟机
virsh undefine win7

参考链接

Arch 文档:

虚拟机时钟问题: